/*
 * This file is part of the Chumby Computer Vision Project: www.EMGRobotics.com
 *
 * Copyright (c) 2010 individual Chumby Computer Vision contributors. 
 * See end of header for contributor list.
 *
 * This code is licensed to you under the terms of the Apache License, version
 * 2.0, or, at your option, the terms of the GNU General Public License,
 * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses,
 * or the following URLs:
 * http://www.apache.org/licenses/LICENSE-2.0
 * http://www.gnu.org/licenses/gpl-2.0.txt
 *
 * If you redistribute this file in source form, modified or unmodified, you
 * may:
 *   1) Leave this header intact and distribute it under the same terms,
 *      accompanying it with the APACHE20 and GPL20 files, or
 *   2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or
 *   3) Delete the GPL v2 clause and accompany it with the APACHE20 file
 *
 * In all cases you must keep this copyright notice intact, along with the
 * list of contributors at the end of this header.
 *
 * Binary distributions must follow the binary distribution requirements of
 * either License.
 *
 * Contributors: 
 *			Eric Gregori ( www.EMGRobotics.com )
 */
//
//**********************************************************************************
//
//  X11 based ChumbyCV algorithm test framework.
//
//  This framework lets you develop and test your algorithm on a Linux dektop.
//
//  Compile this file, with a file containing your algorithm ( see example algorithm files )
//  gcc -L/usr/lib  -lXt -lX11 -lXaw ChumbyCV_X11.c ChumbyCV_V4L2.c ChumbyCV_MotionDetect.c -o ChumbyCV_X11
//  Replace ChumbyCV_Motion.c  with your algorithm file.
//
//  Don't forget to chmod 777 ChumbyCV_X11 after building 
//
//**********************************************************************************
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Porthole.h>

//**********************************************************************************
// Constants
//**********************************************************************************
#define STREAMBUFS	4 		// Number of streaming buffer
#define EMG_WIDTH	320		// Capture Image width
#define EMG_HEIGHT	240		// Capture Image height

#define SETPIXEL(r,g,b)   ((r<<16)|(g<<8)|(b))		

//**********************************************************************************
// Structures
//**********************************************************************************
/* Windows Display Objects */
typedef struct capture_window
{
  XtAppContext *app;
  Widget       w_toplevel;
  Widget       w_viewport;
  GC           imagegc;
  int          x_depth;
}capture_window;

//**********************************************************************************
// External function declared in Algorithm file
//**********************************************************************************
extern uint32_t ChumbyCV_Algorithm( unsigned char* CameraData );
extern int ChumbyCV_Parameters( int argc, char *argv[], int width, int height );
extern void ChumbyCV_ShutDown( void ); 

//**********************************************************************************
// External function declared in V4L2 file
//**********************************************************************************
extern void start_capturing( char* dev_name, int width, int height );
extern void stop_capturing( void );
extern unsigned char *GetFrame( void );
extern void ReleaseFrame( void );

//**********************************************************************************
// Global Buffers
//**********************************************************************************
XImage *capt_ximg[STREAMBUFS];
unsigned long emg_data[640*480]; // use short for RGB565


//*************************************************************************************
//*************************************************************************************
//*******************************                     *********************************
//*******************************  X Display Routines *********************************
//*******************************                     *********************************
//*************************************************************************************
//*************************************************************************************

void
display_free (XImage *XImgTmp)
{
  if (XImgTmp)
    XDestroyImage(XImgTmp);
   
  return;
}

XImage *
display_alloc (capture_window * pcapt_w, char *data, int width, int height)
{
  XImage *XImgTmp;

  printf("\nwidth=%d, height=%d, depth=%d", width, height,  pcapt_w->x_depth );

  XImgTmp = XCreateImage (XtDisplay (pcapt_w->w_viewport),
 	       DefaultVisual (XtDisplay (pcapt_w->w_viewport), 0),
	       (pcapt_w->x_depth == 32) ? 24 : pcapt_w->x_depth,
	       ZPixmap, 0, data, width, height, 16, 0);
	       
  if (XImgTmp == NULL)
    {
      printf ("No XImage\n");
    }
  
  return XImgTmp;
}

int display_get_depth (capture_window * pcapt_w)
{
//  if (pcapt_w->x_depth == 0)
//    {
#ifdef X_DEPTH
      pcapt_w->x_depth = X_DEPTH;
#else
      Display *d = XtDisplay (pcapt_w->w_toplevel);
      Window w = DefaultRootWindow (XtDisplay (pcapt_w->w_toplevel));

      XWindowAttributes wa;
      XGetWindowAttributes (d, w, &wa);
      pcapt_w->x_depth = wa.depth;
#endif
//    }

  printf( "\nX11 reports depth of %d", pcapt_w->x_depth );

  return (pcapt_w->x_depth);
}

int
display_start (int count, 
	       capture_window * pcapt_w, int width, int height)
{
  Arg 	arg[2];
  int	i;

  printf("\nDISPLAY_START: start\n");

  for (i=0; i < count; i++)
    {
      capt_ximg[i] = display_alloc (pcapt_w, &emg_data, 
                                    width, height);
    }
  
  XtSetArg (arg[0], XtNwidth, width);
  XtSetArg (arg[1], XtNheight, height);
  XtSetValues (pcapt_w->w_viewport, arg, 2);
  XtRealizeWidget (pcapt_w->w_toplevel);

  printf("\nDISPLAY_START: end\n");

  return 1;
}

void
display_stop (capture_window * pcapt_w)
{
  return;
}


int
display_init (capture_window * pcapt_w, 
	      char *my_device,
	      int argc, char *argv[])
{
  int width, height;

  printf( "\nDISPLAY_INIT:enter" );

  pcapt_w->app = (XtAppContext *) malloc (sizeof (XtAppContext));
  pcapt_w->w_toplevel = XtAppInitialize (pcapt_w->app, "DTSS", NULL, 0,
					 &argc, argv, NULL, NULL, 0);

  display_get_depth (pcapt_w);
  width = EMG_WIDTH;;
  height = EMG_HEIGHT;

  pcapt_w->w_viewport = XtVaCreateManagedWidget ("port",
						 portholeWidgetClass,
						 pcapt_w->w_toplevel, XtNwidth,
						 width, XtNheight,
						 height, NULL);

  XtRealizeWidget (pcapt_w->w_toplevel);
  pcapt_w->imagegc = XCreateGC (XtDisplay (pcapt_w->w_viewport),
				XtWindow (pcapt_w->w_viewport), 0, NULL);
  if (pcapt_w->imagegc == NULL)
    {
      printf ("XCreateGC failed.\n");
      return 0;
    }

  printf( "\nDISPLAY_INIT: end\n");

  return 1;
}

int
display_status (capture_window * pcapt_w)
{
  XEvent event;

  if (XtIsRealized (pcapt_w->w_viewport))
    {
      return 1;
    }
  else
    {
      XtAppPeekEvent (*pcapt_w->app, &event);
      return 0;
    }

}

void
display_refresh (capture_window * pcapt_w, 
                 XImage *XImgTmp, int width, int height)
{
//  printf("\nDISPLAY_REFRESH %d, %d\n", width, height );

  XPutImage (XtDisplay (pcapt_w->w_viewport),
	     XtWindow (pcapt_w->w_viewport),
	     pcapt_w->imagegc, XImgTmp, 0, 0, 0, 0, width, height);
//  printf( "\nDISPLAY_REFRESH: end" );
}


void
display_wakeup (capture_window * pcapt_w)
{
  XEvent event;

  while (XtAppPending (*pcapt_w->app))
    {
      XtAppNextEvent (*pcapt_w->app, &event);
      switch (event.type)
	{
	  default:
	    break;
	}
      XtDispatchEvent (&event);
    }
}

//*************************************************************************************
//*************************************************************************************
//*******************************                     *********************************
//****************************    Algorithm CallBack      *****************************
//*******************************                     *********************************
//*************************************************************************************
//*************************************************************************************
inline void SetPixel( uint32_t index, unsigned char r, unsigned char g, unsigned char b )
{
	emg_data[index] = SETPIXEL(r,g,b); 
}

//*************************************************************************************
//*************************************************************************************
//*******************************                     *********************************
//*******************************        main         *********************************
//*******************************                     *********************************
//*************************************************************************************
//*************************************************************************************
int main (int argc, char *argv[])
{
	capture_window capt_w;
	uint32_t i;
	unsigned char	*data;
	char my_device[64];

	fprintf( stderr, "\nChumbyCV X11 Algorithm Test Framework\n" );
	if( argc < 2 )
	{
		fprintf( stderr, "\nUsage: %s V4L2_device [parameters]", argv[0] );
		fprintf( stderr, "\nExample: %s /dev/video0\n\n\n", argv[0] );
		exit(1);
	}

	strcpy(my_device, argv[1] );

	// Send command line options to Algorithm
	if( ChumbyCV_Parameters( argc, argv, EMG_WIDTH, EMG_HEIGHT ) )
		exit(1);

  	display_init (&capt_w, my_device, argc, argv);
  	display_start ( STREAMBUFS , &capt_w, EMG_WIDTH, EMG_HEIGHT );
        start_capturing( my_device, EMG_WIDTH, EMG_HEIGHT );

    	for (;;)
    	{
        	if (display_status (&capt_w))
        	{
			data = GetFrame();
			if( data == NULL ) continue;
			// YUYV
			// y1 = data[CaptureIndex++];
			// u  = data[CaptureIndex++];
			// y2 = data[CaptureIndex++];
			// v  = data[CaptureIndex++];
			(void)ChumbyCV_Algorithm( data );
#if 0			
	        	for(i=0, index=0; i<((EMG_WIDTH*EMG_HEIGHT)); i++ )
	        	{
		   		// RGB24 output display - 0x00rrbbgg
		   		temp = data[index+=2];
		   		emg_data[i] = SETPIXEL(temp,temp,temp);
	        	}
#endif
			// update display
	        	display_refresh (&capt_w, capt_ximg[0], EMG_WIDTH, EMG_HEIGHT);

			ReleaseFrame();			
		} // display status

      		display_wakeup (&capt_w);
    	} // for(;;)

        stop_capturing ();
	ChumbyCV_ShutDown();
  	return 0;
}

